/*
 * @Author: 李九阳
 * @Date: 2021-12-13 20:25:26
 * @LastEditors: 李九阳
 * @LastEditTime: 2021-12-14 10:17:43
 */
// 背景：在类电商类项目，往往存在大量的图片，如 banner 广告图，菜单导航图，美团等商家列表头图等。图片众多以及图片体积过大往往会影响页面加载速度，造成不良的用户体验，所以进行图片懒加载优化势在必行。

// 需求：实现一个图片懒加载指令，只加载浏览器可见区域的图片。

// 思路：

// 图片懒加载的原理主要是判断当前图片是否到了可视区域这一核心逻辑实现的
// 拿到所有的图片 Dom ，遍历每个图片判断当前图片是否到了可视区范围内
// 如果到了就设置图片的 src 属性，否则显示默认图片
// 图片懒加载有两种方式可以实现，一是绑定 srcoll 事件进行监听，二是使用 IntersectionObserver 判断图片是否到了可视区域，但是有浏览器兼容性问题。

// 下面封装一个懒加载指令兼容两种方法，判断浏览器是否支持 IntersectionObserver API，如果支持就使用 IntersectionObserver 实现懒加载，否则则使用 srcoll 事件监听 + 节流的方法实现。
import { App } from "vue";
const lazyLoad: any = this;
export default (app: App<Element>) => {
  app.directive("lazyLoad", {
    // install方法
    mounted(Vue: any, options: any) {
      const defaultSrc = options.default;
      Vue.directive("lazy", {
        bind(el: any, binding: any) {
          lazyLoad.init(el, binding.value, defaultSrc);
        },
        inserted(el: any) {
          if (IntersectionObserver) {
            lazyLoad.observe(el);
          } else {
            lazyLoad.listenerScroll(el);
          }
        },
      });
    },
    // 初始化
    init(el: any, val: any, def: any) {
      el.setAttribute("data-src", val);
      el.setAttribute("src", def);
    },
    // 利用IntersectionObserver监听el
    observe(el: any) {
      const io = new IntersectionObserver((entries) => {
        const realSrc = el.dataset.src;
        if (entries[0].isIntersecting) {
          if (realSrc) {
            el.src = realSrc;
            el.removeAttribute("data-src");
          }
        }
      });
      io.observe(el);
    },
    // 监听scroll事件
    listenerScroll(el: any) {
      const handler = lazyLoad.throttle(lazyLoad.load, 300);
      lazyLoad.load(el);
      window.addEventListener("scroll", () => {
        handler(el);
      });
    },
    // 加载真实图片
    load(el: any) {
      const windowHeight = document.documentElement.clientHeight;
      const elTop = el.getBoundingClientRect().top;
      const elBtm = el.getBoundingClientRect().bottom;
      const realSrc = el.dataset.src;
      if (elTop - windowHeight < 0 && elBtm > 0) {
        if (realSrc) {
          el.src = realSrc;
          el.removeAttribute("data-src");
        }
      }
    },
    // 节流
    throttle(fn: any, delay: any) {
      let timer: any;
      let prevTime: any;
      return (...args: any) => {
        const currTime = Date.now();
        const context: any = this;
        if (!prevTime) prevTime = currTime;
        clearTimeout(timer);

        if (currTime - prevTime > delay) {
          prevTime = currTime;
          fn.apply(context, args);
          clearTimeout(timer);
          return;
        }

        timer = setTimeout(function () {
          prevTime = Date.now();
          timer = null;
          fn.apply(context, args);
        }, delay);
      };
    },
  });
};
